<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";
import {createGhostTexture} from "@/book/ThreeUtils";

const canvas = ref();

const scene = new THREE.Scene();

const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(30, 30, 150);
camera.lookAt(0, 0, 0);

const orbitControl = new OrbitControls(camera, renderer.domElement);

const stat = Stats();

const controls = {
  size : 15,
  transparent : true,
  opacity : 0.6,
  color : 0xffffff,
  rotate : true,
  sizeAttenuation : true,
  switchCreate : 'point',
  redraw : () => {
    if (scene.getObjectByName("points")) {
      scene.remove(scene.getObjectByName("points"));
    }
    // createPoints(controls.size, controls.transparent, controls.opacity, controls.sizeAttenuation,
    //     controls.color);
    if (controls.switchCreate === 'point') {
      createPoints(controls.size, controls.transparent, controls.opacity, controls.sizeAttenuation,
          controls.color);
    } else {
      createSprites();
    }
  },
};

const gui = new GUI();
// @ts-ignore
gui.add(controls, 'size', 0, 20).onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'transparent').onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'opacity', 0, 1).onChange(controls.redraw);
// @ts-ignore
gui.addColor(controls, 'color').onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'sizeAttenuation').onChange(controls.redraw);
// @ts-ignore
gui.add(controls, 'rotate');
// @ts-ignore
gui.add(controls, 'switchCreate',['point', 'sprite']).onChange(controls.redraw);

// point 模拟 粒子效果
function createPoints(size, transparent, opacity, sizeAttenuation, color) {
  const geom = new THREE.BufferGeometry();
  const material = new THREE.PointsMaterial({
    size: size,
    transparent: transparent,
    opacity: opacity,
    map: createGhostTexture(),
    sizeAttenuation: sizeAttenuation,
    color: color
  });

  const points = [];
  const range = 500;
  for (let i = 0; i < 5000; i++) {
    points.push(Math.random() * range - range / 2, Math.random() * range - range / 2, Math.random() * range - range / 2);
  }

  geom.setAttribute("position", new THREE.BufferAttribute(new Float32Array(points), 3));
  const cloud = new THREE.Points(geom, material);
  cloud.name = 'points';
  scene.add(cloud);
}

function createSprites() {
  const material = new THREE.SpriteMaterial({
    map: createGhostTexture(),
    color: 0xffffff
  });

  const group = new THREE.Group();
  group.name = "points";
  const range = 500;
  for (let i = 0; i < 1500; i++) {
    const sprite = new THREE.Sprite(material);
    sprite.position.set(Math.random() * range - range / 2, Math.random() * range - range / 2, Math.random() *
        range - range / 2);
    sprite.scale.set(4, 4, 4);
    group.add(sprite);
  }
  scene.add(group);
}

controls.redraw();

let step = 0;
const render = () => {
  stat.update();
  orbitControl.update();

  if (controls.rotate && scene.getObjectByName("points")) {
    step += 0.001;
    const obj = scene.getObjectByName("points");
    obj.rotation.x = step;
    obj.rotation.z = step;
  }

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);

  render();
});
</script>
